using System;
using System.Collections;
using System.Collections.Specialized;
using System.Collections.Generic;
using System.Text;
using System.Xml;
using System.Configuration;
using System.Reflection;
using System.IO;
using System.Web;

namespace Pampa.CRUD.Configuration
{
    /// <summary>
    /// Esta clase administra los xml de configuracion del CRUD
    /// Los componentes de visualizacion y edicion interactuaran con ella para solicitar 
    /// la configuracion de una entidad determinada
    /// </summary>
    public static class ConfigurationManager
    {
        static ConfigurationManager()
        {
        }

        public static EntityConfiguration GetEditConfiguration(string entityName)
        {
            XmlDocument document = GetXml(entityName);
            XmlNode node = GetChildNode(document.DocumentElement, "edit");

            return new EntityConfiguration(node);
        }

        public static EntityConfiguration GetViewConfiguration(string entityName)
        {
            XmlDocument document = GetXml(entityName);
            XmlNode node = GetChildNode(document.DocumentElement, "view");

            return new EntityConfiguration(node);
        }

        public static XmlNode GetGridXmlConfiguration(string entityName)
        {
            XmlDocument document = GetXml(entityName);
            XmlNode node = GetChildNode(document.DocumentElement, "grid");

            return node;
        }

        public static XmlNode GetChildNode(XmlNode node, string xmlPath)
        {
            XmlDocument document = node.OwnerDocument;
            XmlNamespaceManager nsManager = new XmlNamespaceManager(document.NameTable);
            nsManager.AddNamespace("pampa", node.NamespaceURI);

            return node.SelectSingleNode("pampa:" + xmlPath, nsManager);
        }

        private static XmlDocument GetXml(string entityName)
        {
            Type entityType = Type.GetType(entityName);

            if (entityType != null)
            {
                return GetXmlByEntityType(entityType);
            }
            else
            {
                return GetXmlByAlias(entityName);
            }
        }

        private static XmlDocument GetXmlByEntityType(Type baseEntityType)
        {
            Type entityType = baseEntityType;
            XmlDocument document = null;
            bool findOut = false;

            // Busco una definicion para la entidad o alguno de sus parents
            while ((!findOut) && (entityType != null))
            {
                try 
                { 
                    document = GetXmlByAlias(entityType.Name); 
                }
                catch (Pampa.CRUD.Exceptions.ConfigurationException) 
                { 
                }

                findOut = (document != null);

                if (!findOut)
                    entityType = entityType.BaseType;
            }

            if (!findOut)
            {
                throw (new Pampa.CRUD.Exceptions.ConfigurationException("No se ha encontrado una definicion para " + baseEntityType.Name));
            }
            else
            {
                return document;
            }
        }

        private static XmlDocument GetXmlByAlias(string name)
        {
            NameValueCollection properties = (NameValueCollection)System.Configuration.ConfigurationManager.GetSection("pampa");
            string assemblyName = properties["crud.entities.assembly"];
            string path = properties["crud.entities.path"];

            if (assemblyName == null)
            {
                return LoadXmlFromFile(path, name);  
            }
            else
            {
                return LoadXmlFromAssembly(assemblyName, path, name);     
            }
        }

        private static XmlDocument LoadXmlFromFile(string path, string entityName)
        {
            string appPath = HttpContext.Current.Request.ApplicationPath;
            string rootPath = HttpContext.Current.Request.MapPath(appPath);

            string fileName = rootPath + "\\" + path + "\\" + entityName + ".crud.xml";

            XmlDocument doc = new XmlDocument();

            try
            {
                doc.Load(fileName);
            }
            catch(Exception)
            {
                throw (new Pampa.CRUD.Exceptions.ConfigurationException("No se pudo cargar el archivo " + fileName));
            }

            return doc;
        }

        private static XmlDocument LoadXmlFromAssembly(string assemblyName, string path, string entityName)
        {
            string fileName = assemblyName + "." + path + "." + entityName + ".crud.xml";

            Assembly assembly = Assembly.Load(new AssemblyName(assemblyName));
            Stream stream = assembly.GetManifestResourceStream(fileName);

            if (stream == null)
            {
                throw (new Pampa.CRUD.Exceptions.ConfigurationException("No se ha encontrado el archivo " + fileName));
            }
            else
            {
                return LoadXmlFromStream(stream);
            }      
        }

        private static XmlDocument LoadXmlFromStream(Stream stream)
        {
            string content = string.Empty;
            using (System.IO.StreamReader sr = new System.IO.StreamReader(stream))
            {
                content = sr.ReadToEnd();
            }

            XmlDocument doc = new XmlDocument();
            doc.LoadXml(content);

            return doc;
        }
    }
}
